﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Collections;
using TotalCrytography.Protector;

namespace TotalCrytography.Protector
{
    class RSA_Algorithm
    {
        public static string encryptString(string inputString, string xmlPublicString)
        {
            // TODO: Add Proper Exception Handlers
            string bitStrengthString = xmlPublicString.Substring(0, xmlPublicString.IndexOf("</BitStrength>") + 14);
            xmlPublicString = xmlPublicString.Replace(bitStrengthString, "");
            int dwKeySize = Convert.ToInt32(bitStrengthString.Replace("<BitStrength>", "").Replace("</BitStrength>", ""));

            RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
            rsaCryptoServiceProvider.FromXmlString(xmlPublicString);
            int keySize = dwKeySize / 8;
            byte[] bytes = Encoding.UTF32.GetBytes(inputString);
            // The hash function in use by the .NET RSACryptoServiceProvider here is SHA1
            // int maxLength = ( keySize ) - 2 - ( 2 * SHA1.Create().ComputeHash( rawBytes ).Length );
            int maxLength = keySize - 42;
            int dataLength = bytes.Length;
            int iterations = dataLength / maxLength;
            StringBuilder stringBuilder = new StringBuilder();

            for (int i = 0; i <= iterations; i++)
            {
                byte[] tempBytes = new byte[(dataLength - maxLength * i > maxLength) ? maxLength : dataLength - maxLength * i];
                Buffer.BlockCopy(bytes, maxLength * i, tempBytes, 0, tempBytes.Length);
                byte[] encryptedBytes = rsaCryptoServiceProvider.Encrypt(tempBytes, true);
                // Be aware the RSACryptoServiceProvider reverses the order of encrypted bytes after encryption and before decryption.
                // If you do not require compatibility with Microsoft Cryptographic API (CAPI) and/or other vendors.
                // Comment out the next line and the corresponding one in the DecryptString function.
                Array.Reverse(encryptedBytes);
                // Why convert to base 64?
                // Because it is the largest power-of-two base printable using only ASCII characters
                stringBuilder.Append(Convert.ToBase64String(encryptedBytes));
            }
            return stringBuilder.ToString();
        }

        public static string decryptString(string inputString, string xmlPrivateString)
        {
            // TODO: Add Proper Exception Handlers
            string bitStrengthString = xmlPrivateString.Substring(0, xmlPrivateString.IndexOf("</BitStrength>") + 14);
            xmlPrivateString = xmlPrivateString.Replace(bitStrengthString, "");
            int dwKeySize = Convert.ToInt32(bitStrengthString.Replace("<BitStrength>", "").Replace("</BitStrength>", ""));

            RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
            rsaCryptoServiceProvider.FromXmlString(xmlPrivateString);
            int base64BlockSize = ((dwKeySize / 8) % 3 != 0) ? (((dwKeySize / 8) / 3) * 4) + 4 : ((dwKeySize / 8) / 3) * 4;
            int iterations = inputString.Length / base64BlockSize;
            ArrayList arrayList = new ArrayList();
            for (int i = 0; i < iterations; i++)
            {
                byte[] encryptedBytes = Convert.FromBase64String(inputString.Substring(base64BlockSize * i, base64BlockSize));
                // Be aware the RSACryptoServiceProvider reverses the order of encrypted bytes after encryption and before decryption.
                // If you do not require compatibility with Microsoft Cryptographic API (CAPI) and/or other vendors.
                // Comment out the next line and the corresponding one in the EncryptString function.
                Array.Reverse(encryptedBytes);
                arrayList.AddRange(rsaCryptoServiceProvider.Decrypt(encryptedBytes, true));
            }
            return Encoding.UTF32.GetString(arrayList.ToArray(Type.GetType("System.Byte")) as byte[]);
        }

        public static string[] generateKey(int keylength)
        {
            RSACryptoServiceProvider RSAProvider = new RSACryptoServiceProvider(keylength);
            string[] strOut = new string[2];
            strOut[0] = "<BitStrength>" + keylength.ToString() + "</BitStrength>" + RSAProvider.ToXmlString(true);
            strOut[1] = "<BitStrength>" + keylength.ToString() + "</BitStrength>" + RSAProvider.ToXmlString(false);
            return strOut;
        }

        public static string signString(string inputString, string hashAlgorithm, string xmlString)
        {
            string bitStrengthString = xmlString.Substring(0, xmlString.IndexOf("</BitStrength>") + 14);
            xmlString = xmlString.Replace(bitStrengthString, "");
            int dwKeySize = Convert.ToInt32(bitStrengthString.Replace("<BitStrength>", "").Replace("</BitStrength>", ""));

            byte[] hashedData = Hash_Algorithm.CreateHashInBytes(inputString, hashAlgorithm);
            RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
            rsaCryptoServiceProvider.FromXmlString(xmlString);
            byte[] signatureBytes = rsaCryptoServiceProvider.SignHash(hashedData, CryptoConfig.MapNameToOID(hashAlgorithm));
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(Convert.ToBase64String(signatureBytes));
            return stringBuilder.ToString();
        }

        public static bool verifySignature(string inputString, string hashAlgorithm, string xmlString, string signature)
        {
            string bitStrengthString = xmlString.Substring(0, xmlString.IndexOf("</BitStrength>") + 14);
            xmlString = xmlString.Replace(bitStrengthString, "");
            int dwKeySize = Convert.ToInt32(bitStrengthString.Replace("<BitStrength>", "").Replace("</BitStrength>", ""));

            byte[] hashedData = Hash_Algorithm.CreateHashInBytes(inputString, hashAlgorithm);
            byte[] signatureBytes = Convert.FromBase64String(signature);
            RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
            rsaCryptoServiceProvider.FromXmlString(xmlString);
            return rsaCryptoServiceProvider.VerifyHash(hashedData, CryptoConfig.MapNameToOID(hashAlgorithm), signatureBytes);
        }
    }
}
